Solid Texturing in Polyray Alexander Enzmann This document describes how to go about defining and developing solid textures for the Polyray raytracer. The "noise surface" is a general purpose 3D solid texturing operation. The options specified in the declaration allow a great deal of control over the coloring of a surface and over the bumpiness of a surface. The complexity and speed of rendering of the noise surface type lies between the standard shading model and the "special" surfaces. Definitions: The terms "vexper" stands for a vector expression of some sort (like <0,0,1> or 2*<3,1,1>, etc.) and "fexper" stands for a floating point expression of some sort (like 1, 2.0 * (3 - 4), etc.) A color is defined by red, green, and blue values from zero to 1, inclusive. A color can appear as a vector expression like: <1, 0, 0> (for red), or if it is one of the defined colors (see color.doc) then the name of the color can be used. A noise surface declaration has the form: texture { noise surface { // Standard shading model values color vexper // Color to use for amb/diff/.. ambient fexper // Amount of ambient diffuse fexper // Amount of diffuse specular vexper, fexper // Color and amount of highlights transmission vexper, fexper, fexper // Color, amount, ior of transparency reflection fexper // Amount of reflectivity microfact kind angle // Specular model and falloff angle color_map(map_entries) // Lookup table for surface color // Solid texturing functions position_fn index // Which position position_scale fexper // Scaling factor for position turbulence fexper // Amount noise affects position octaves fexper // # of octaves of noise lookup_fn index // Which color lookup function // Surface bumpiness/ripple modifiers normal_fn index // Which type of bumpiness bump_scale fexper // Bumpiness of the surface frequency fexper // Frequency of ripples phase fexper // Offset of ripple wavefronts } } Not all of the components need to be specified. If components are left out, then the associated effects will not be applied. For example if "normal_fn" is not specified then there will be no modification of the normal to the surface - it will stay as smooth as it started. Color Maps Each entry in a color map has the form: [start_value, end_value, start_color, end_color] or [start_value, end_value, start_color, start_alpha, end_color, end_alpha] When Polyray looks for a color in a color map it starts with an index value generated by the 3D texturing function defined by the surface. If this value is between "start_value" and "end_value", then a resulting color is calculated that is between "start_color" and "end_color". It is also possible to specify transparency ("alpha") values for each color in the color map. This value ranges from 0 (not transparent at all) to 1 (fully transparent). A color map that uses transparency would appear like this: color_map( [-2, 0, blue, 0.8, blue, 0.8] [-1, 0.5, green, 0.5, green, 0.5] [ 0.5, 1.0, green, 0.5, tan, 0] [ 1.0, 1.7, tan, tan] [ 1.7, 2, white, white]) Making use of the alpha value is a little more subtle than it may first appear. The reason for this is that during shading the "color" part is used for the ambient and diffuse shading, then the alpha is used to determine how much of the color "behind" the surface will be added to the final color. Added to this is that the color coming from behind the surface is filtered by the surface color - if you have a transparent green in front of a transparent blue then you will not see anything through the two surfaces, all color would be filtered out. In order to help with transparency in noise surfaces, it is possible to specify values for the "transmission" component of the shading. If this part of the texture is defined, then the color part of the transmission will be used for the filter. A typical declaration would be: transmission white, 1, 1 Which will prevent Polyray from filtering the transmitted light. What you will get is the sum of the surface color and the color that is coming from behind the surface. Solid Texturing Functions The way the final color of the texture is decided is by calculating a floating point value using the following general formula: index = lookup_fn(position_scale * position_fn + turbulence * noise3d(P, octaves)) The index value that is calculated is then used to lookup a color from the color map. This final color is used for the ambient, diffuse, reflection and transmission filters. The functions that are currently available, with their corresponding indices are: Position functions: Index Effect 1 x value in the object coordinate system 2 x value in the world coordinate system 3 Distance from the z axis 4 Distance from the origin 6 Distance around the y axis (from 0 -> 1, counterclockwise) default: 0.0 Lookup functions: Index Effect 1 sawtooth function, result from 0 -> 1 2 sin function, result from 0->1 3 ramp function, result from 0->1 default: no modification made Definitions of these function numbers that make sense are: define position_plain 0 define position_objectx 1 define position_worldx 2 define position_cylindrical 3 define position_spherical 4 define position_radial 5 define lookup_plain 0 define lookup_sawtooth 1 define lookup_sin 2 define lookup_ramp 3 Normal Modifying Functions define plain_normal 0 define bump_normal 1 define ripple_normal 2 define dented_normal 3 Marble-like Textures // The standard sort of marble texture define white_marble texture { noise surface { color white ambient 0.3 diffuse 0.8 specular 0.3 microfacet Reitz 5 position_fn position_objectx lookup_fn lookup_sawtooth octaves 3 turbulence 3 color_map( [0.0, 0.8, <1, 1, 1>, <0.6, 0.6, 0.6>] [0.8, 1.0, <0.6, 0.6, 0.6>, <0.1, 0.1, 0.1>]) } } // Nice blue agate texture define sapphire_agate texture { noise surface { ambient 0.5 diffuse 0.7 position_fn position_objectx position_scale 1.1 lookup_fn lookup_sawtooth octaves 3 turbulence 2 color_map( [0.0, 0.3, <0, 0, 0.9>, <0, 0, 0.8>] [0.3, 1, <0, 0, 0.8>, <0, 0, 0.4>]) } scale <0.5, 0.5, 0.5> } Wood-like Textures // Create a wood texture. Concentric rings of color // are wrapped around the z-axis. There is some turbulence // in order to keep the rings from looking too perfect. define light_wood <0.6, 0.24, 0.1> define median_wood <0.3, 0.12, 0.03> define dark_wood <0.05, 0.01, 0.005> define wooden texture { noise surface { // Define the standard shading components ambient 0.2 diffuse 0.7 specular white, 0.5 microfacet Reitz 10 // Define how coloring is applied position_fn position_cylindrical // Color lookup is rings about z-axis position_scale 1 // This affects spacing between rings lookup_fn lookup_sawtooth // Keep the lookup value from 0->1 octaves 1 // Keep the turbulence smooth turbulence 1 // Keep rings from being too circular // Define how the colors change from ring to ring color_map( [0.0, 0.2, light_wood, light_wood] [0.2, 0.3, light_wood, median_wood] [0.3, 0.4, median_wood, light_wood] [0.4, 0.7, light_wood, light_wood] [0.7, 0.8, light_wood, median_wood] [0.8, 0.9, median_wood, light_wood] [0.9, 1.0, light_wood, dark_wood]) } } Gradient Textures // This is an example of a gradient texture. define mountain_colors texture { noise surface { ambient 0.2 diffuse 0.8 specular 0.2 position_fn position_objectx color_map( [-128, 0, blue, blue] [ 0, 20, green, green] [ 20, 40, green, tan] [ 40, 90, tan, tan] [ 90, 128, white, white]) } // The texture is rotated 90 degrees so that is is applied along the // y-axis rather than along the x-axis. This is so that the coloring // will correspond to height. rotate <0, 0, 90> } Generic Turbulent Textures // Simple color map texture define whorl_texture texture { noise surface { color green ambient 0.3 diffuse 0.8 lookup_fn lookup_sawtooth octaves 2 turbulence 2 color_map( [0.0, 0.3, green, blue] [0.3, 0.6, blue, skyblue] [0.6, 0.8, skyblue, orange] [0.8, 1.0, orange, red]) } scale <0.5, 0.5, 0.5> } Cloudy Sky Texture // Defines a turbulent texture with colors that go from grey in // the middle of the clouds, white at the edges, and blue elsewhere. // To use it, make a big sphere (possibly squashing it so the top isn't // too far away), apply this texture with scaling values that spread // the clouds to the desired amount. define cloudy_sky texture { noise surface { ambient 0.9 diffuse 0 specular 0 turbulence 6.0 position_fn position_worldx lookup_fn 1 octaves 4 color_map( [0.0, 0.6, <0.4, 0.4, 0.4>, <1, 1, 1>] [0.6, 0.8, <1, 1, 1>, <0.196078, 0.6, 0.8>] [0.8, 1.0, <0.196078, 0.6, 0.8>, <0.196078, 0.6, 0.8>]) } } Ripple (wavy) Textures An example of a texture that uses ripples is: define blue_ripple texture { noise surface { // Define the standard shading components color <0.4, 0.4, 1.0> ambient 0.3 diffuse 0.4 specular white, 0.7 reflection 0.5 microfacet Reitz 10 // Define how how the surface will be bumped normal ripple_normal // Make a wavy surface frequency 100 // This results in fairly tight ripples bump_scale 2 // Make reasonably large bumps } scale <10, 1, 10> } This sample combines a marble coloration with rippling: define ripple_marble_texture texture { noise surface { color white position_fn 1 lookup_fn 1 octaves 4 turbulence 3 normal 2 frequency 10 bump_scale 5 ambient 0.1 diffuse 0.5 specular 0.6 microfacet Reitz 10 color_map( [0.0, 0.8, <1, 1, 1>, <0.6, 0.6, 0.6>] [0.8, 1.0, <0.6, 0.6, 0.6>, <0.1, 0.1, 0.1>]) } translate <-5, 0, 0> }